package com.spzx.product.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.spzx.common.core.exception.ServiceException;
import com.spzx.product.domain.*;
import com.spzx.product.mapper.ProductDetailsMapper;
import com.spzx.product.mapper.ProductMapper;
import com.spzx.product.mapper.ProductSkuMapper;
import com.spzx.product.mapper.SkuStockMapper;
import com.spzx.product.service.ProductService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

@Slf4j
@Service
public class ProductServiceImpl extends ServiceImpl<ProductMapper, Product> implements ProductService {

	@Autowired
	private ProductSkuMapper productSkuMapper;

	@Autowired
	private ProductMapper productMapper;

	@Autowired
	private SkuStockMapper skuStockMapper;

	@Autowired
	private ProductDetailsMapper productDetailsMapper;

	@Autowired
	private RedisTemplate redisTemplate;

	// 查询商品列表
	@Override
	public List<Product> selectProductList(Product product) {
		List<Product> list = baseMapper.selectProductList(product);
		return list;
	}

	// 添加商品
	@Transactional
	@Override
	public int insertProduct(Product product) {
		// 0 操作多张表，添加到事务里面
		// 1 添加商品基本信息到product表
		baseMapper.insert(product);

		// 2 获取前端传递过来商品所有sku列表list集合 productSkuList
		List<ProductSku> productSkuList = product.getProductSkuList();

		// 3 把sku列表list集合遍历，得到每个sku对象，把每个sku添加product_sku
		for (int i = 0; i < productSkuList.size(); i++) {
			ProductSku productSku = productSkuList.get(i);
			// 设置一些sku其他值
			productSku.setSkuCode(product.getId() + "_" + i);
			productSku.setProductId(product.getId());
			String skuName = product.getName() + " " + productSku.getSkuSpec();
			productSku.setSkuName(skuName);
			// 把每个sku添加product_sku表
			productSkuMapper.insert(productSku);

			// 4 把每个sku和库存量，添加sku_stock
			SkuStock skuStock = new SkuStock();
			skuStock.setSkuId(productSku.getId());
			skuStock.setTotalNum(productSku.getStockNum());
			skuStock.setLockNum(0);
			skuStock.setAvailableNum(productSku.getStockNum());
			skuStock.setSaleNum(0);
			skuStockMapper.insert(skuStock);
		}

		// 5 添加商品product_details
		// 商品图片 image_urls
		// detailsImageUrlList集合 转换字符串
		ProductDetails productDetails = new ProductDetails();

		List<String> detailsImageUrlList = product.getDetailsImageUrlList();
		// [01.jpg,02.jpg]  ==  01.jpg,02.jpg
		productDetails.setProductId(product.getId());

		String detailsImageUrl = String.join(",", detailsImageUrlList);
		productDetails.setImageUrls(detailsImageUrl);

		productDetailsMapper.insert(productDetails);

		return product.getId().intValue();
	}

	// 获取商品详细信息
	@Override
	public Product selectProductById(Long id) {
		// 1 根据商品id获取商品基本信息，查询product表
		Product product = baseMapper.selectById(id);

		// 2 根据商品id获取商品所有sku列表
		LambdaQueryWrapper<ProductSku> wrapper = new LambdaQueryWrapper<>();
		wrapper.eq(ProductSku::getProductId, id);
		List<ProductSku> productSkuList = productSkuMapper.selectList(wrapper);

		// 2.1 因为每个sku有对应库存量
		// 2.2 查询每个sku库存量封装到每个ProductSku对象里面
		// 第一种 productSkuList遍历，得到每个sku，根据每个skuid查询库存表得到库存量，设置对象
//        productSkuList.forEach(productSku -> {
//            Long skuId = productSku.getId();
//            //根据skuId查询库存
//            LambdaQueryWrapper<SkuStock> wrapper1 = new LambdaQueryWrapper<>();
//            wrapper1.eq(SkuStock::getSkuId,skuId);
//            SkuStock skuStock = skuStockMapper.selectOne(wrapper1);
//            Integer totalNum = skuStock.getTotalNum();
//
//            productSku.setStockNum(totalNum);
//        });

		// 第二种 stream流数据处理
		// 1 从productSkuList获取所有id
		// List<ProductSku>
		List<Long> skuIdList =
				productSkuList.stream().map(ProductSku::getId).collect(Collectors.toList());

		// 2 拿着所有skuId集合查询库存表，对应库存信息
		LambdaQueryWrapper<SkuStock> wrapperSkuStock = new LambdaQueryWrapper<>();
		wrapperSkuStock.in(SkuStock::getSkuId, skuIdList);
		List<SkuStock> skuStockList = skuStockMapper.selectList(wrapperSkuStock);

		// 3 第二步查询sku所有库存信息list ，转换map集合
		// key:skuId  value:库存量
		Map<Long, Integer> map =
				skuStockList.stream()
						.collect(Collectors.toMap(SkuStock::getSkuId, SkuStock::getTotalNum));

		// 4 把productSkuList遍历，根据skuId到第三步map里面找到对应库存，设置到productSku
		productSkuList.forEach(productSku -> {
			// 获取skuId
			Long productSkuId = productSku.getId();
			// 根据skuid查询map，获取对应库存量
			Integer num = map.get(productSkuId);
			productSku.setStockNum(num);
		});

		// 3 把查询商品所有sku列表封装product里面
		product.setProductSkuList(productSkuList);

		// 4 根据商品id获取商品详情数据  图片列表
		LambdaQueryWrapper<ProductDetails> wrapperDetails = new LambdaQueryWrapper<>();
		wrapperDetails.eq(ProductDetails::getProductId, id);
		ProductDetails productDetails = productDetailsMapper.selectOne(wrapperDetails);

		String imageUrls = productDetails.getImageUrls();
		// 01.jpg,02.jpg
		// 字符串 imageUrls 转换数组
		String[] split = imageUrls.split(",");
		// 把数组转换list集合
		List<String> list = Arrays.asList(split);
		// 5 把查询商品图片列表封装product里面
		product.setDetailsImageUrlList(list);

		// 6 返回封装完成product对象
		return product;
	}

	// 修改
	@Transactional
	@Override
	public int updateProduct(Product product) {
		// 修改商品信息
		baseMapper.updateById(product);

		List<ProductSku> productSkuList = product.getProductSkuList();

		productSkuList.forEach(productSku -> {
			// 修改商品SKU信息
			productSkuMapper.updateById(productSku);

			// 修改商品库存
			SkuStock skuStock =
					skuStockMapper.selectOne(new LambdaQueryWrapper<SkuStock>()
													 .eq(SkuStock::getSkuId, productSku.getId()));
			skuStock.setTotalNum(productSku.getStockNum());

			int availableNum = skuStock.getTotalNum() - skuStock.getLockNum();

			skuStock.setAvailableNum(availableNum);
			skuStockMapper.updateById(skuStock);
		});

		// 修改商品详细信息
		ProductDetails productDetails =
				productDetailsMapper.selectOne(new LambdaQueryWrapper<ProductDetails>().eq(ProductDetails::getProductId, product.getId()));
		productDetails.setImageUrls(String.join(",", product.getDetailsImageUrlList()));
		productDetailsMapper.updateById(productDetails);
		return 1;
	}

	// 删除
	@Transactional
	@Override
	public int deleteProductByIds(Long[] ids) {
		// 1 根据商品id删除product
		baseMapper.deleteBatchIds(Arrays.asList(ids));

		// 2 删除skuId，删除sku里面库存信息
		// 获取商品所有skuId
		LambdaQueryWrapper<ProductSku> wrapper = new LambdaQueryWrapper<>();
		wrapper.in(ProductSku::getProductId, ids);
		List<ProductSku> productSkuList = productSkuMapper.selectList(wrapper);

		// productSkuList  获取skuId值
		List<Long> skuIdList =
				productSkuList.stream().map(ProductSku::getId).collect(Collectors.toList());

		// 删除sku对应库存 删除条件 skuId
		LambdaQueryWrapper<SkuStock> wrapperSkuStock = new LambdaQueryWrapper<>();
		wrapperSkuStock.in(SkuStock::getSkuId, skuIdList);
		skuStockMapper.delete(wrapperSkuStock);

		// 3 根据商品id删除product_sku
		productSkuMapper
				.delete(new LambdaQueryWrapper<ProductSku>().in(ProductSku::getProductId, ids));

		// 4 根据商品id删除详情
		productDetailsMapper
				.delete(new LambdaQueryWrapper<ProductDetails>()
								.in(ProductDetails::getProductId, ids));
		return 1;
	}

	// 查询畅销商品
	@Override
	public List<ProductSku> getTopSale() {
		return productSkuMapper.selectTopSale();
	}

	// 远程调用使用，商品列表
	@Override
	public List<ProductSku> selectProductSkuList(SkuQuery skuQuery) {
		return productSkuMapper.selectProductSkuList(skuQuery);
	}

	// 获取sku详情
	@Override
	public ProductSku getProductSku(Long skuId) {
		return productSkuMapper.selectById(skuId);
	}

	// TODO 技术点: 添加缓存和分布式锁
	public ProductSku getProductSkuV2(Long skuId) {
		// 1. 先看redis里面是否存在
		String dataKey = "product:sku:" + skuId;
		ProductSku productSku = (ProductSku) redisTemplate.opsForValue().get(dataKey);
		// 2. 如果存在, 直接返回
		if (productSku != null) {
			return productSku;
		}
		// 3. 如果不存在, 从数据库里面拿
		// 3.1 加锁, 防止击穿
		String lockKey = "lock:product:sku:" + skuId;
		// 加UUID作为锁标识, 防止被其他线程释放
		String uuid = UUID.randomUUID().toString().replaceAll("-", "");
		// 加锁
		Boolean isSuccess = redisTemplate.opsForValue().setIfAbsent(lockKey, uuid, 10, TimeUnit.SECONDS);
		if (isSuccess) {
			// 如果获取到锁
			try {
				// 如果成功, 从数据库中拿的时候再看看缓存中有没有
				productSku = (ProductSku) redisTemplate.opsForValue().get(dataKey);
				// 缓存中没有, 从数据库中拿取
				if (productSku == null) {
					productSku = productSkuMapper.selectById(skuId);
					// 放入缓存中
					redisTemplate.opsForValue().set(dataKey, productSku);
					return productSku;
				}
			} finally {
				// 删除锁, 使用lua脚本保证释放锁的原子性
				String scriptText = "if redis.call(\"get\",KEYS[1]) == ARGV[1]\n" +
											"then\n" +
											"    return redis.call(\"del\",KEYS[1])\n" +
											"else\n" +
											"    return 0\n" +
											"end";
				DefaultRedisScript<Long> redisScript = new DefaultRedisScript<>();
				redisScript.setScriptText(scriptText);
				redisScript.setResultType(Long.class);
				redisTemplate.execute(redisScript, Arrays.asList(lockKey), uuid);
			}
		} else {
			try {
				TimeUnit.MILLISECONDS.sleep(200L);
			} catch (InterruptedException e) {
				throw new RuntimeException(e);
			}
			// 没有获取到, 自旋
			return getProductSkuV2(skuId);
		}
		return productSku;
	}

	@Override
	public List<SkuPrice> getSkuPriceList(List<Long> skuIds) {
		List<SkuPrice> skuPriceList = new ArrayList<>();
		for (Long skuId : skuIds) {
			SkuPrice skuPrice = getSkuPrice(skuId);
			if (skuPrice != null) {
				skuPriceList.add(skuPrice);
			}
		}
		return skuPriceList;
	}

	// 检查库存
	@Override
	public String checkAndLock(String orderNo, List<SkuLockVo> skuLockVoList) {
		// 1. 先查询商品库存是否充足
		skuLockVoList.forEach(skuLockVo -> {
			Long skuId = skuLockVo.getSkuId();
			Integer skuNum = skuLockVo.getSkuNum();
			// 查询库存是否充足
			SkuStock skuStock = skuStockMapper.checkStock(skuId, skuNum);
			if (skuStock == null) {
				skuLockVo.setIsHaveStock(false);
			} else {
				skuLockVo.setIsHaveStock(true);
			}
		});
		// 收集库存不足的商品
		List<SkuLockVo> noHaveStock = skuLockVoList.stream().filter(item -> !item.getIsHaveStock()).toList();
		// 检查商品的库存是否充足
		if (!noHaveStock.isEmpty()) {
			StringBuilder sb = new StringBuilder();
			noHaveStock.forEach(item -> {sb.append("商品：").append(item.getSkuId()).append("库存不足; ");});
			return sb.toString();
		} else {
			// 2. 如果充足, 则进行库存锁定
			skuLockVoList.forEach(skuLockVo -> {
				Long skuId = skuLockVo.getSkuId();
				Integer skuNum = skuLockVo.getSkuNum();
				// 锁定库存
				int row = skuStockMapper.lockStock(skuId, skuNum);
				if (row == 0) {
					throw new ServiceException("库存不足");
				}

			});
		}
		String dataKey = "order:lock:data:" + orderNo;
		redisTemplate.opsForValue().set(dataKey, skuLockVoList);
		return "";
	}

	@Override
	public void unLockProductStock(String orderNo) {
		String dataKey = "order:lock:data:" + orderNo;
		List<SkuLockVo> skuLockVoList = (List<SkuLockVo>) redisTemplate.opsForValue().get(dataKey);
		if (skuLockVoList != null && !skuLockVoList.isEmpty()) {
			skuLockVoList.forEach(skuLockVo -> {
				Long skuId = skuLockVo.getSkuId();
				Integer skuNum = skuLockVo.getSkuNum();
				// 解锁库存
				int row = skuStockMapper.unLockStock(skuId, skuNum);
				if (row == 0) {
					redisTemplate.delete(dataKey);
					throw new ServiceException("解锁库存失败");
				}
			});
		}
		redisTemplate.delete(dataKey);
	}

	@Override
	public void minusStock(String orderNo) {
		String dataKey = "order:lock:data:" + orderNo;
		List<SkuLockVo> skuLockVoList = (List<SkuLockVo>) redisTemplate.opsForValue().get(dataKey);
		if (skuLockVoList != null && !skuLockVoList.isEmpty()) {
			skuLockVoList.forEach(skuLockVo -> {
				Long skuId = skuLockVo.getSkuId();
				Integer skuNum = skuLockVo.getSkuNum();
				// 减库存
				int row = skuStockMapper.minusStock(skuId, skuNum);
				if (row == 0) {
					redisTemplate.delete(dataKey);
					throw new ServiceException("扣减库存失败");
				}
			});
		}
	}

	@Override
	public SkuPrice getSkuPrice(Long skuId) {
		LambdaQueryWrapper<ProductSku> queryWrapper
				= new LambdaQueryWrapper<ProductSku>()
						  .select(ProductSku::getId,
								  ProductSku::getSalePrice,
								  ProductSku::getMarketPrice)
						  .eq(ProductSku::getId, skuId);
		ProductSku productSku = productSkuMapper.selectOne(queryWrapper);
		return new SkuPrice(productSku.getId(), productSku.getSalePrice(), productSku.getMarketPrice());

	}

	@Override
	public ProductDetails getDetails(Long productId) {
		LambdaQueryWrapper<ProductDetails> queryWrapper
				= new LambdaQueryWrapper<ProductDetails>()
						  .select(ProductDetails::getId,
								  ProductDetails::getImageUrls)
						  .eq(ProductDetails::getProductId, productId);
		return productDetailsMapper.selectOne(queryWrapper);
	}

	@Override
	public Map<String, Long> getSkuSpecValue(Long productId) {
		// // 1. 先根据商品id查询下面的所以SKU信息
		LambdaQueryWrapper<ProductSku> queryWrapper
				= new LambdaQueryWrapper<ProductSku>()
						  .select(ProductSku::getId, ProductSku::getSkuSpec)
						  .eq(ProductSku::getProductId, productId);
		return productSkuMapper.selectList(queryWrapper).stream()
					   .collect(Collectors.toMap(ProductSku::getSkuSpec, ProductSku::getId));
		// 2. 根据SKU id列表查询SKU规格信息

		// LambdaQueryWrapper<ProductSpec> queryWrapper2
		// 		= new LambdaQueryWrapper<ProductSpec>()
		// 				  .select(ProductSpec::getId, ProductSpec::getSpecValue)
		// 				  .eq(ProductSpec::getId, productId);

	}

	@Override
	public SkuStockVo getSkuStock(Long skuId) {
		LambdaQueryWrapper<SkuStock> queryWrapper
				= new LambdaQueryWrapper<SkuStock>()
						  .select(SkuStock::getSkuId, SkuStock::getAvailableNum, SkuStock::getSaleNum)
						  .eq(SkuStock::getSkuId, skuId);
		SkuStock skuStock = skuStockMapper.selectOne(queryWrapper);
		return new SkuStockVo(skuStock.getSkuId(), skuStock.getAvailableNum(), skuStock.getAvailableNum());
	}

}
